#问题： 什么是元组拆包？

'''
元组拆包其实是从元组中提取出不同端的信息，我们可以将这一操作理解为平行赋值。
也就是说拆包会把一个可迭代对象中的元素一并赋值到对应变量中去。
这样做可以让我们把注意力集中在我们所需要关注的元素上面。
'''
tuple = ('beijing','2019','8.7')
city,year,date = tuple
print (city,year,date)

#拆包时我们还可以用 * 来处理剩下的元素，在 python 里常用这种方法来代表不确定数量参数！

a, b, *rest = range(5)
print(a) # 0
print(b) # 1
print(rest) # [2,3,包]

#使用占位符 _ 去忽略不需要关注的元素，相当于一个占位符，抵消掉了我们不需要关注的元素。
x, _, y = (1, 2, 3)
print(x) # 1
print(y) # 3

#同时使用 * 和占位符 _ 来看看：

x, *_, y = (1, 2, 3, 4, 5)
print(x) # 1
print(y) # 5


#问题：*args 和 **kwargs 的用法
'''
当函数的参数不确定时候可以用 *args 和 **kwargs。*args 是用于接收多余的未命名参数，**kwargs 用于接收实参中的命名参数，
其中 args 是一个元组类型，而 kwargs 是一个字典类型的数据。形参中的 *args 把传进来的数据放在了 args 这个元组中。
把 **kwargs 传进来的数据放在了 kwargs 这个字典中。
'''

print ("*args 其实就是把元组中的数据进行拆包，也就是把元组中的数据拆成单个数据")

def print_everything(*args):
    for count, thing in enumerate(args):
        print( '{0}. {1}'.format(count, thing))
print_everything('apple', 'banana', 'cabbage')

print("**kwargs 就是把字典中的数据进行拆包，传进的命名数据可以从 kwargs 按键进行索引获得。")

def table_things(**kwargs):
    for name, value in kwargs.items():
       print( '{0} = {1}'.format(name, value))
table_things(apple = 'fruit', cabbage = 'vegetable')

print ("如果同时使用 *args 和 **kwargs")

def func(required_arg, *args, **kwargs):
    # required_arg是一个常规参数
    print (required_arg)

    # args是一个元组
    if args:
        print (args)

    # kwargs是一个字典，里面的键值就是传进来的参数变量名
    if kwargs:
        print (kwargs)

#func() #直接调用 func() 函数会报错：func（）至少需要1个参数（给定0）
#func("required argument") # 调用 func() 函数的时候给定了一个参数，所以只会打印一个参数。
#func("required argument", 1, 2, '3') ## **args 接受了剩余的参数并放到了元组中
func("required argument", 1, 2, '3', keyword1=4, keyword2="foo") # *args接受了无 Key 值的参数并放到元组中，**kwargs 接收了有 key 值的参数并放到了字典中。
